#region Copyright (c) 2006-2012 nHydrate.org, All Rights Reserved
//--------------------------------------------------------------------- *
//                          NHYDRATE.ORG                                *
//             Copyright (c) 2006-2012 All Rights reserved              *
//                                                                      *
//                                                                      *
//This file and its contents are protected by United States and         *
//International copyright laws.  Unauthorized reproduction and/or       *
//distribution of all or any portion of the code contained herein       *
//is strictly prohibited and will result in severe civil and criminal   *
//penalties.  Any violations of this copyright will be prosecuted       *
//to the fullest extent possible under law.                             *
//                                                                      *
//THE SOURCE CODE CONTAINED HEREIN AND IN RELATED FILES IS PROVIDED     *
//TO THE REGISTERED DEVELOPER FOR THE PURPOSES OF EDUCATION AND         *
//TROUBLESHOOTING. UNDER NO CIRCUMSTANCES MAY ANY PORTION OF THE SOURCE *
//CODE BE DISTRIBUTED, DISCLOSED OR OTHERWISE MADE AVAILABLE TO ANY     *
//THIRD PARTY WITHOUT THE EXPRESS WRITTEN CONSENT OF THE NHYDRATE GROUP *
//                                                                      *
//UNDER NO CIRCUMSTANCES MAY THE SOURCE CODE BE USED IN WHOLE OR IN     *
//PART, AS THE BASIS FOR CREATING A PRODUCT THAT PROVIDES THE SAME, OR  *
//SUBSTANTIALLY THE SAME, FUNCTIONALITY AS THIS PRODUCT                 *
//                                                                      *
//THE REGISTERED DEVELOPER ACKNOWLEDGES THAT THIS SOURCE CODE           *
//CONTAINS VALUABLE AND PROPRIETARY TRADE SECRETS OF NHYDRATE,          *
//THE REGISTERED DEVELOPER AGREES TO EXPEND EVERY EFFORT TO             *
//INSURE ITS CONFIDENTIALITY.                                           *
//                                                                      *
//THE END USER LICENSE AGREEMENT (EULA) ACCOMPANYING THE PRODUCT        *
//PERMITS THE REGISTERED DEVELOPER TO REDISTRIBUTE THE PRODUCT IN       *
//EXECUTABLE FORM ONLY IN SUPPORT OF APPLICATIONS WRITTEN USING         *
//THE PRODUCT.  IT DOES NOT PROVIDE ANY RIGHTS REGARDING THE            *
//SOURCE CODE CONTAINED HEREIN.                                         *
//                                                                      *
//THIS COPYRIGHT NOTICE MAY NOT BE REMOVED FROM THIS FILE.              *
//--------------------------------------------------------------------- *
#endregion
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Windows.Forms;
using System.Text.RegularExpressions;

namespace Acme.Northwind.Install
{
	/// <summary>
	/// The database installer class
	/// </summary>
	[RunInstaller(true)]
	public partial class DatabaseInstaller : Installer
	{
		private const string EXENAME_KEY = "-EXENAME";
		private const string DLLNAME_KEY = "-DLLNAME";
		private const string DEFAULT_APPDB_KEY = "-CONNECTIONSTRING";
		private const string DEFAULT_MASTERDB_KEY = "-MASTERDB";

		private string[] PARAMKEYS_UPGRADE = new string[] { "/upgrade" };
		private string[] PARAMKEYS_CREATE = new string[] { "/create" };
		private string[] PARAMKEYS_MASTERDB = new string[] { "master", "masterdb", DEFAULT_MASTERDB_KEY };
		private string[] PARAMKEYS_APPDB = new string[] { "applicationdb", "connectionstring", DEFAULT_APPDB_KEY };
		private string[] PARAMKEYS_HELP = new string[] { "/help", "/h", "/?" };

		/// <summary>
		/// The default constructor
		/// </summary>
		public DatabaseInstaller()
		{
			InitializeComponent();
		}

		/// <summary>
		/// Performs an install of a database
		/// </summary>
		public override void Install(System.Collections.IDictionary stateSaver)
		{
			base.Install(stateSaver);

			var commandParams = GetCommandLineParameters();
			if (PrintHelp(commandParams))
				return;

			var appDbConnectionString = GetAppDbString(commandParams);
			var masterDbConnectionString = GetMasterDbConnectionString(commandParams);
			var isUpgrade = string.IsNullOrEmpty(masterDbConnectionString);
			isUpgrade = GetUpgradeDbSetting(commandParams, isUpgrade);

			if ((isUpgrade && !string.IsNullOrEmpty(appDbConnectionString)) || (!isUpgrade && !string.IsNullOrEmpty(appDbConnectionString) && !string.IsNullOrEmpty(masterDbConnectionString)))
			{
				CommandLineInstall(masterDbConnectionString, appDbConnectionString, isUpgrade);
			}
			else
			{
				UIInstall();
			}
		}

		/// <summary>
		/// Performs an install of a database
		/// </summary>
		public void Install(string masterConnection, string databaseName, string connectionString, bool isUpgrade)
		{
			//base.Install(stateSaver);
			CommandLineInstall(masterConnection, connectionString, isUpgrade);
		}

		private string GetDbName(string newDatabaseConnectionString)
		{
			if (!string.IsNullOrEmpty(newDatabaseConnectionString))
			{
				Regex regEx = new Regex(@"(Initial Catalog\s*=|Database\s*=)\s*([\w-]+)[;]?");
				Match firstMatch = regEx.Match(newDatabaseConnectionString);
				if (firstMatch != null && firstMatch.Groups != null && firstMatch.Groups.Count >= 3)
				{
					return firstMatch.Groups[2].Value;
				}
				else
				{
					throw new Exception("Could not find database name in connecton string : " + newDatabaseConnectionString);
				}
			}
			else
			{
				throw new Exception("Empty database connection string : " + newDatabaseConnectionString);
			}
		}

		private bool GetUpgradeDbSetting(Dictionary<string, string> commandParams, bool defaultValue)
		{
			bool retVal = defaultValue;
			foreach (string s in PARAMKEYS_UPGRADE)
			{
				if (commandParams.ContainsKey(s))
				{
					retVal = true;
					break;
				}
			}
			return retVal;
		}

		private string GetMasterDbConnectionString(Dictionary<string, string> commandParams)
		{
			string retVal = string.Empty;
			foreach (string s in PARAMKEYS_MASTERDB)
			{
				if (commandParams.ContainsKey(s))
				{
					retVal = commandParams[s];
					break;
				}
			}
			return retVal;
		}

		private string GetAppDbString(Dictionary<string, string> commandParams)
		{
			string retVal = string.Empty;
			foreach (string s in PARAMKEYS_APPDB)
			{
				if (commandParams.ContainsKey(s))
				{
					retVal = commandParams[s];
					break;
				}
			}
			return retVal;
		}

		private bool PrintHelp(Dictionary<string, string> commandParams)
		{
			bool retVal = false;
			foreach (string s in PARAMKEYS_HELP)
			{
				if (commandParams.ContainsKey(s))
				{
					retVal = true;
					Console.WriteLine("Help Coming Soon!");
					break;
				}
			}
			return retVal;
		}

		private Dictionary<string, string> GetCommandLineParameters()
		{
			var retVal = new Dictionary<string, string>();
			var args = Environment.GetCommandLineArgs();

			var loopcount = 0;
			foreach (var arg in args)
			{
				var regEx = new Regex(@"-(\w+)(:(.*))?");
				var regExMatch = regEx.Match(arg);
				if (regExMatch.Success)
				{
					retVal.Add(regExMatch.Groups[1].Value, regExMatch.Groups[3].Value);
				}
				else
				{
					var tmpKey = Guid.NewGuid().ToString();
					if (loopcount == 0)
						tmpKey = EXENAME_KEY;
					else if (loopcount == 1)
						tmpKey = DLLNAME_KEY;
				}
				loopcount++;
			}

			return retVal;
		}

		/// <summary>
		/// Provides functionality to perform installs from a command line
		/// </summary>
		/// <param name="masterConnectionString">The connection information to the SQL Server master database</param>
		/// <param name="newDatabaseConnectionString">The connection string to the newly created database</param>
		/// /// <param name="isUpgrade">Determines if this is a database upgrade</param>
		public void CommandLineInstall(string masterConnectionString, string newDatabaseConnectionString, bool isUpgrade)
		{
			if (isUpgrade)
			{
				#region Upgrade
				try
				{
					UpgradeInstaller.UpgradeDatabase(newDatabaseConnectionString, false);
				}
				catch
				{
					System.Threading.Thread.Sleep(8000);
					try
					{
						UpgradeInstaller.UpgradeDatabase(newDatabaseConnectionString, false);
					}
					catch
					{
						throw;
					}
				}
				#endregion
			}
			else
			{
				#region Create
				if (this.Action == ActionTypeConstants.Create)
				{
					string newDatabaseName = GetDbName(newDatabaseConnectionString);
					SqlServers.CreateDatabase(masterConnectionString, newDatabaseName);
					var startTime = DateTime.Now;
					while (DateTime.Now.Subtract(startTime).TotalSeconds < 10)
					{
						Application.DoEvents();
					}
				}

				try
				{
					UpgradeInstaller.UpgradeDatabase(newDatabaseConnectionString, true);
				}
				catch
				{
					System.Threading.Thread.Sleep(8000);
					try
					{
						UpgradeInstaller.UpgradeDatabase(newDatabaseConnectionString, true);
					}
					catch
					{
						throw;
					}
				}
				#endregion
			}
		}

		/// <summary>
		/// 
		/// </summary>
		private void UIInstall()
		{
			if (IdentifyDatabaseConnectionString())
			{
				if (this.Action == ActionTypeConstants.Create)
				{
					UpgradeInstaller.UpgradeDatabase(this.Settings.GetPrimaryConnectionString(), true);
				}
				else if (this.Action == ActionTypeConstants.Upgrade)
				{
					UpgradeInstaller.UpgradeDatabase(this.Settings.GetPrimaryConnectionString(), false);
				}
				else if (this.Action == ActionTypeConstants.AzureCopy)
				{
					UpgradeInstaller.AzureCopyDatabase(this.Settings);
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="savedState"></param>
		public override void Uninstall(System.Collections.IDictionary savedState)
		{
			base.Uninstall(savedState);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		private bool IdentifyDatabaseConnectionString()
		{
			var F = new IdentifyDatabaseForm();
			if (F.ShowDialog() == DialogResult.OK)
			{
				this.Action = F.Action;
				this.Settings = F.Settings;
				return true;
			}
			return false;
		}

		public InstallSettings Settings { get; private set; }
		public ActionTypeConstants Action { get; private set; }

	}
}